#include "../../lib/algo/Graham.h"

void Graham::applyGraham(vector<Travail> _travails){

	vector<Travail> travails = _travails;
	vector<Affectation> vAffectation;
//	map<string, float> mExec;
	FFMMap nbrMLibre;
	float minBis = 0;
	float count;

	printf("dans Graham size travaux = >  : %d\n", travails.size());
//	Graham::RecupererHeure();
	Graham::timeToDate(0.00);
	// "tri bull" pour avoir la liste decrementale (Graham 66): 
	int size = travails.size();
	Travail tmp;
	for (int a = 0; a < size - 1; a++){
		for (int b = 1; b < size; b++){
			if (travails[b].getNbrMachine() > travails[b-1].getNbrMachine()){
				tmp = travails[b];
				travails[b] = travails[b-1];
				travails[b-1] = tmp;
			}
		}
	}
// Debogage °°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°
	for (int i = 0; i < travails.size(); i++){
		cout<< "element TT - " << travails[i].getDuree1();
		cout<< "  element ID - " << travails[i].getIdTravail();
		cout<< "  element PC - " << travails[i].getNbrMachine() <<endl;	
	}
// Debogage °°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°

// ============= Execution des travaux ====================

// ============= Fonction pour rechercher =================
// ============= le temps Minimum () ======================
	string s;
	Exec mExec;

	mExec.insert(Exec::value_type("0", 0.00));		
	mExec.insert(Exec::value_type("1", 0.00));		
	mExec.insert(Exec::value_type("2", 0.00));		
	mExec.insert(Exec::value_type("3", 0.00));		
	mExec.insert(Exec::value_type("4", 0.00));		
	mExec.insert(Exec::value_type("5", 0.00));		
	mExec.insert(Exec::value_type("6", 0.00));		
	mExec.insert(Exec::value_type("7", 0.00));		
	mExec.insert(Exec::value_type("8", 0.00));		

//	cout << "size mExec : " << mExec.size() << endl;
	
	Exec::iterator pos;
	for(pos = mExec.begin(); pos != mExec.end(); ++pos) {
//		cout << "key: " << pos->first << "\t";
//        	cout << "value: " << pos->second << endl;
    	}

// ========================================================
	vector<string> listeMachineLibres;
	vector<string> elisteMachineLibres;	
	
	for (int i = 0; i < travails.size(); i++) {
// ============== Appeler findMin() =======================
		float tempsMin = Graham::findMin(mExec, minBis);
//		cout << "Min = " << tempsMin << endl;
//		cout << "minBis = " << minBis << endl;
 		count = compter(mExec, tempsMin);

		nbrMLibre.insert(make_pair(count,tempsMin)); //construire le MMap: "key" = nbr des Machines avec le temps min = "value"

		FFMMap::iterator pos1;
		float nbrM = 0; //nbr des machines disponible
		for (pos1 = nbrMLibre.begin(); pos1 != nbrMLibre.end(); ++pos1) {
			nbrM += (float)pos1->first;
//		        cout <<"res du MMap : "  << pos1->second << endl;
    		}

// ============== Appeler faireExecuter() =================
		if (travails[i].getNbrMachine() <= nbrM){
//			cout << "tx machines = " << travails[i].getNbrMachine() << "\t nbrM dispo = " << nbrM <<endl;
//			cout << "nbrM is OK" << endl;
			listeMachineLibres = Graham::listeMachineLibre(nbrMLibre, mExec, nbrM);
//			cout << "listMachineLibres : " << listeMachineLibres.size() <<endl;
			mExec = Graham::faireExecuter(mExec, listeMachineLibres, travails[i], vAffectation);
			listeMachineLibres.clear();
			nbrMLibre.clear();
		}
		else {
//			listeMachineLibres.clear();
		while (nbrM < travails[i].getNbrMachine()){
	
/*			float min = Graham::findMin(mExec, minBis);
			cout << "min = " << min << endl;*/
			
			Graham::findMinBis(mExec, minBis, tempsMin);
//			cout << "minBis = " << minBis << endl;
		
			tempsMin = minBis;
			count = compter(mExec, tempsMin);
//			cout << "count = " << count << endl;

 			nbrMLibre.insert(make_pair(count,tempsMin));
			for (pos1 = nbrMLibre.begin(); pos1 != nbrMLibre.end(); ++pos1) {
//			        cout <<"nbr Machine dans nbeMLibre : "  << pos1->first << endl;
	    		}
			nbrM += count;
//			cout << "nbrM WHILE = " << nbrM << endl;
	
		}//fin WHILE

		listeMachineLibres = Graham::listeMachineLibre(nbrMLibre, mExec, nbrM);
		mExec = Graham::faireExecuter(mExec, listeMachineLibres, travails[i], vAffectation);

		listeMachineLibres.clear();
		nbrMLibre.clear();

//			int encore = travails[i].getNbrMachine() - listeMachineLibres.size();
//			listeMachineLibres = Graham::encoreMachineLibre(mExec, listeMachineLibres, tempsMin, encore);
//			cout << "trouve encore " << elisteMachineLibres.size() << " machines libres " << endl;
//			Graham::ajousterTemps(mExec, listeMachineLibres);
//			mExec = Graham::faireExecuter(mExec, listeMachineLibres, travails[i]);
		}//fin ELSE
	}//fin FOR TX

	cout << "fin :::::::::::::::: " << vAffectation.size() << endl;
// Debogage °°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°
//	printf("apres avoir trie la liste -decrementale-\n");
	for (int i = 0; i < travails.size(); i++){
//		cout<< "Duree - " << travails[i].getDuree1() <<endl;
//		cout<< "Machines - " << travails[i].getNbrMachine() <<endl;
	}
// Debogage °°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°
//	cout<< "cMax = " << cMax <<endl;
}

// ============= Fonction de findMin() ====================
//  cette fonction trouve le temps minimum c.a.d. 	===
//  les machines qui finisse les travaux le plus tot 	===
// ========================================================

float Graham::findMin(Exec mExec, float & minBis){
	Exec::iterator pos;
	float min = 999999;

	for(pos = mExec.begin(); pos != mExec.end(); ++pos) {
		if (pos->second <= min){
			min = pos->second;
		}
    	}//fin FOR
//	minBis = min;
	return min;
}

// ============= Fonction de findMinBis() ====================
//  cette fonction trouve le temps de fin du ravail de la ====
//  machine suivante, dont le tmps plus proche de min ========
// ===========================================================

float Graham::findMinBis(Exec mExec, float & minBis, float _min){
	Exec::iterator pos;
	float min = _min;
	minBis = 9999999;

	for(pos = mExec.begin(); pos != mExec.end(); ++pos) {
		if (pos->second > min & minBis >= pos->second){
//			min = pos->second;
			minBis = pos->second;
		}
    	}//fin FOR
//	cout << "dans minBis : minBis = " << minBis << endl;
	return min;
}

// ============= Fonction de compter() ====================
//  cette fonction compte le nombre de machines ===========
//  avec le meme temps 	===================================
// ======================================================
float Graham::compter(Exec mExec, float _tempsMin){
	Exec::iterator pos;
	float min = _tempsMin;
	float count = 0;

	for(pos = mExec.begin(); pos != mExec.end(); ++pos) {
		if (pos->second == min){
			count++;
		}
    	}//fin FOR
	return count;
}

// ============= Fonction listeMachineLibre()==============
//  cette fonction return les IP de machines	===========
//  qui finissent leurs travail à temps Min	===========
//  calcule par la fonction findMin()		===========
// ========================================================

vector<string> Graham::listeMachineLibre(FFMMap nbrMLibres, Exec mExec, float nbrMachine){

	FFMMap::iterator pos1;
	FFMMap::reverse_iterator rpos; 
	Exec::iterator pos2;
	vector<string> listeMachineLibre;
	string ipMachine;

/*			for (pos1 = nbrMLibres.begin(); pos1 != nbrMLibres.end(); ++pos1) {
			        cout <<"nbr Machine dans nbeMLibres --- : "  << pos1->first << endl;
	    		}
*/
	for(rpos = nbrMLibres.rbegin(); rpos != nbrMLibres.rend(); ++rpos) {
		float t = rpos->second;
//		cout << "Graham::listeMachineLibre : time = " << t << endl;
		for (pos2 = mExec.begin(); pos2 != mExec.end(); ++pos2) {
			if (pos2->second == t){
				ipMachine = pos2->first;
				listeMachineLibre.push_back(ipMachine);
			}//fin IF
		}//fin FOR II listeMachineLibre
    	}//fin FOR I
//	cout << "Graham::listeMachineLibre : liste size = " << listeMachineLibre.size() << endl;
/*	for (int i = 0; i < listeMachineLibre.size(); i++) {	
		cout << "Graham::listeMachineLibre : ip " << listeMachineLibre[i] << endl;
	}*/
	return listeMachineLibre;
}

// ============= Fonction faireExecuter() =================
//  cette fonction execute les travaux ====================
// ========================================================

map<string, float> Graham::faireExecuter(Exec mExec, vector<string> lML, Travail travail, vector<Affectation> & vAffectation){
	
	Exec::iterator pos, pos2;
//	vector<Affectation> vAffectation;
	int duree = travail.getDuree1();
	int nbrMachine = travail.getNbrMachine();
	int idTravail = travail.getIdTravail();
//	cout<<"nbrMachine besoin = "<< nbrMachine <<endl;
	vector<string> ips;
	string ip;
	string str;
	string date = "05/45";
	float time, max = 0;
//	str = lML[0];

//	cout << "faireExec : tavidan MAX = : " << max << endl;
	for (int i = 0; i < lML.size(); i++){
//		cout << "lML ip : " << lML[i] << endl;
	}
//	cout << "premier temps de lML dans FExec : " << str << endl;
	//besoin du temps sur lequel on va ajuster les autres
	for (int i = 0; i < lML.size(); i++){
		str = lML[i];
	for(pos = mExec.begin(); pos != mExec.end(); ++pos) {
		if (pos->first == str & pos->second > max){
			max = pos->second;
		}
	}//fin FOR 
	}
//	cout << "lML size : " << lML.size() << endl;
//	cout << "premier temps de lML dans FExec MAX: " << max << endl;
	float dDebut;
	for(int i = 0; i < nbrMachine; i++){
//		mExec.insert(Exec::value_type(lML[i], duree));		

		str = lML[i]; //recuperer ip de la machine libre
		ip = mExec.find(str)->first; //recuperer ip de machine a la qulle on va affecter le travail
		//recuperer le temps(quand il fini le travail precedant) de machine a qulle on va affecter le travail
		time = mExec.find(str)->second; 
		dDebut = time;
		mExec.erase(str); //effacer l'element trouver, pour inserer nouvelle element 

		// la partie ou on ajouste le temps quand cette machine fini son dernier travail
		time = time + (max - time);

		time += duree; //augmanter le temps
		
//		cout<<"dans faireExecuter ip ajuster = " << ip <<endl;
//		cout<<"dans faireExecuter time ajuster = " << time <<endl;

		mExec.insert(Exec::value_type(ip, time)); //inserer l'element changé
//		mAff.insert(ExecStr::value_type(ip, date));
//		cout << "IP :: " << ip << endl;
		ips.push_back(ip);
	}//fin FOR
// Creer les Objets Affectations ::::::::::::::::::::
//		cout << "ips " << ips.size() << endl;
		Affectation aff;
		for (int i = 0;  i < ips.size(); i++){
			aff.setIpMachine(ips[i]);
			aff.setIdTravail(idTravail);
			aff.setDateDebut(Graham::timeToDate(dDebut));
			vAffectation.push_back(aff);
		}	
		ips.clear();
//		cout << "IP :: " << ip << endl;
///		cout << "ID :: " << aff.getIdTravail() << endl;
//		cout << "dateDebut :: " << aff.getDateDebut() << endl;
	
//#####################################################
	// Debogage °°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°
		for (pos2 = mExec.begin(); pos2 != mExec.end(); ++pos2) {
//			cout << "ip : " << pos2->first << "\t time : " << pos2->second << endl;
		}//fin FOR
		cout << endl;
//		cout << "''''''''" << 
	// Debogage °°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°
	return mExec;
}

// ============= Fonction de encoreMachineLibre() ====================
//  cette fonction trouve les machines qui se liberent apres ===========
//  les machines qui finisse les travaux le plus tot 	==============
// ===================================================================

vector<string> Graham::encoreMachineLibre(Exec & mExec, vector<string> lML, float tempsMin, int encore){
	Exec::iterator pos;
	float min = tempsMin;
	vector<string> listeMachineLibre = lML;
	string ipMachine;
	int count = 0; //variable commptant le nbr des machines supplemantaires
	float procheTemps; //variable ou on memorise le temps le plus proche de tempsMin
	float differenceMin, difference = 9999; //variable ou on memorise la differance entre tempsMin et ptocheTemps

//	cout<<"encore : lml.size = " << listeMachineLibre.size() <<endl;
//	cout<<"on a besoin encore : encore = " << encore <<endl;

	while (count < encore) {
	for(pos = mExec.begin(); pos != mExec.end(); ++pos) {
		if (pos->second > min){
			differenceMin = pos->second - min;
				if (differenceMin < difference){
					procheTemps = pos->second;
				}
		}
	}//fin FOR	
	for(pos = mExec.begin(); pos != mExec.end(); ++pos) {
		if (procheTemps  == pos->second & count < encore){
			ipMachine = pos->first;
			listeMachineLibre.push_back(ipMachine);
			count++;	
//			pos->second += diferenceMin; 
		}
	}//fin FOR
//	cout << "on a trouve les machine libres avec le temps plus poche nbr. = " << count << endl;
    	}
//	cout << "count = " << count << endl;
	return listeMachineLibre;
}

// ============= Fonction ajousterTemps() =================
//  fonction qui fait augmanter le temps ==================
//  de fin pour les nouvelles machines qu'on a ============
//  trouve sur les quels on va executer le Tx =============
// ========================================================

void Graham::ajousterTemps(Exec & mExec, vector<string> lML){

	string ip;
	float temps, max = 0.0;
	Exec::iterator pos;
// °°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°
	for (int j = 0; j < lML.size(); j++){
//		cout<<"ip "<<lML[j]<<endl;
	}
// °°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°
	for (int i = 0; i < lML.size(); i++){
		ip= lML[i];
		for(pos = mExec.begin(); pos != mExec.end(); ++pos) {
			if (pos->first == ip){
				temps = pos->second;
				if (temps > max){
					max = temps;
				}
			}
		}//fin FOR II
	}//fin FOR I

	for (int i = 0; i < lML.size(); i++){
		ip= lML[i];
		for(pos = mExec.begin(); pos != mExec.end(); ++pos) {
			if (pos->first == ip){
				pos->second = pos->second + (max - pos->second);
			}
		}//fin FOR II
	}//fin FOR I

	for(pos = mExec.begin(); pos != mExec.end(); ++pos) {
		cout << "Nkey -- : " << pos->first << "\t";
        	cout << "Nvalue -- : " << pos->second << endl;
    	}

}

string Graham::RecupererHeure(){

	time_t now;
	time(&now);
	string str = ctime(&now);

	return str;
}

string Graham::timeToDate(float time){
	string date;
	int hh = 0;
	int mm = 0;
	int jj = 0;
	int mois = 0; 
	int aa = 0;

	int min = 60, heure = 24, jour = 30; 
	int itime = (int)time;
// recuperation de la date du systeme
	string sysDate = Graham::RecupererHeure();
//	cout << "date : " << sysDate << endl;
	string dMM = sysDate.substr(14, 2);
	string dHH = sysDate.substr(11, 2);
	string dJJ = sysDate.substr(8, 2);
	string dMois = "02";

//	cout << "dMM : " << dMM << endl;
//	cout << "dHH : " << dHH << endl;
//	cout << "dJJ : " << dJJ << endl;
//	cout << "dMois : " << dMois << endl;
//######################################

//String to int :: 
//	string some_string = "23";
	istringstream buffer_MM(dMM);
	istringstream buffer_HH(dHH);
	istringstream buffer_JJ(dJJ);
	istringstream buffer_Mois(dMois);

	int dMM_int, dHH_int, dJJ_int, dMois_int;

	buffer_MM >> dMM_int;
	buffer_HH>> dHH_int;
	buffer_JJ >> dJJ_int;
	buffer_Mois >> dMois_int;
//#####################################

// Calculer les MM, HH, JJ ...
// somme des minutes (systeme_minutes + travail_duree)
	mm = dMM_int;
//	cout << "sysTime " << dMM_int << endl;	 
	int sumMM = dMM_int + itime;
//	cout << "itime " << itime << endl;
//	cout << "sumMM : " << sumMM << endl;
	// HH & MMin
	if (sumMM > 60){	
		hh = sumMM / 60;
		mm = sumMM % min; 
//		cout << "if shsi ....." << endl;
	}	
//	cout  << " mm  " << mm << endl;
// somme des HH :
	hh = hh + dHH_int;
	// jj & MMois
	if (hh > 24){	
		jj = hh / 24;
		hh = hh % heure;
	}	
// somme des JJ :
	jj = jj + dJJ_int;
	// MMois & AA
	if (jj > 30){	
		mois = jj / 30;
		jj = jj % jour;
	}
// somme des JJ :
	mois = mois + dMois_int;
//	cout << "\nmois : " << mois <<endl;
//	cout << "jj : " << jj <<endl;
//	cout << "hh : " << hh <<endl;
//	cout << "mm : " << mm <<endl;
//#####################################	

// int to string
// verifier O9 et 9 ... pour les mois et jours (doit etre sur 2 chifres)
	ostringstream buffer_mm;
	ostringstream buffer_hh;
	ostringstream buffer_jj;
	ostringstream buffer_mois;
	
	string str_mm;
	string str_hh;
	string str_jj;
	string str_mois;

// pour les minutes 
	if (mm < 10){
		buffer_mm << mm;
		str_mm = buffer_mm.str();
		str_mm = "0" + str_mm;
	}
	else {
		buffer_mm << mm;
		str_mm = buffer_mm.str();		
	}
// pour les heures 
	if (hh < 10){
		buffer_hh << hh;
		str_hh = buffer_hh.str();
		str_hh = "0" + str_hh;
	}
	else {
		buffer_hh << hh;
		str_hh = buffer_hh.str();		
	}
// pour les jours 
	if (jj < 10){
		buffer_jj << jj;
		str_jj = buffer_jj.str();
		str_jj = "0" + str_jj;
	}
	else {
		buffer_jj << jj;
		str_jj = buffer_jj.str();		
	}
// pour les mois 
	if (mois < 10){
		buffer_mois << mois;
		str_mois = buffer_mois.str();
		str_mois = "0" + str_mois;
	}
	else {
		buffer_mois << mois;
		str_mois = buffer_mois.str();		
	}


	date = str_jj + "/" + str_mois + "/" + "2007" + " " + str_hh + ":" + str_mm;
//	cout << "date XML : " << date << endl;
//#####################################

	return date;
}


